/***********************************************************************************
 * 文件名： mcu_spi.c
 * 作者： 刘言
 * 版本： 1
 * 说明：
 * 		CST92F25 的 SPI 驱动。CST92F25 虽然有2个SPI，但是同一时刻只能有1个SPI工作，故没有必
 *  要使用另一个SPI，看作是只有1个SPI的MCU，多器件连接时使用片选选择器件即可。
 *      仅配置 SPI0 外设，需要自行配置管脚映射，仅支持 SPI0 ，注意正确配置管脚映射。
 *      仅实现了主机模式。硬件支持从机模式。
 *      不依赖SDK官方SPI驱动。
 * 修改记录：
 * 	2022/10/10: 初版。 刘言。
***********************************************************************************/
#include "mcu_spi.h"
#include "mcu_top.h"

#ifndef REG_PERIPHERAL_MASTER_SELECT
#define REG_PERIPHERAL_MASTER_SELECT    (*((volatile unsigned int *) 0x4000302C))
#define SPI_MASTER_SELECT(num)	        ((REG_PERIPHERAL_MASTER_SELECT) |= (BIT(num) | BIT(num + 4)))
#define SPI_SLAVE_SELECT(num)	        ((REG_PERIPHERAL_MASTER_SELECT) &= ~(BIT(num)))
#endif

// 以下配置项为内部配置项，不建议修改

#define _SPI_SRL        0
#define _SPI_SLV_OE     0
#define _SPI_FRF        0
#define _SPI_DFS        SPI_1BYTE
#define _SPI_TMOD       0       // 0: TXRX; 1: TX; 2: RX. 传输模式  
#define _SPI_IE_MST     0       // 1：使能 多主机连接（总线错误） 中断
#define _SPI_IE_RXF     0       // 1：使能 接收FIFO满 中断
#define _SPI_IE_RXO     0       // 1：使能 接收FIFO溢出 中断
#define _SPI_IE_RXU     0       // 1：使能 接收FIFO下溢 中断
#define _SPI_IE_TXO     0       // 1：使能 发送FIFO溢出 中断
#define _SPI_IE_TXE     0       // 1：使能 发送FIFO空 中断

// 内部变量定义
#if (_SPI_USE_ASNYC)
static volatile bool mBusy = false;
static spi_cb_t mNotifyEvent = NULL;
static u8 * mTxBuff = NULL;
static u8 * mRxBuff = NULL;
static u16 mTxLen = 0;          // 当前已发送长度（仅已写入到硬件FIFO中的数据量，不能判断硬件已经发送完毕）
static u16 mRxLen = 0;          // 当前已接收长度 (已读取到缓存的数据量)
static u16 mTransLen = 0;       // 本次要传输的数据长度
#endif


// 内部函数声明

#if (_SPI_USE_ASNYC)
static void TransCompleted();
#if _SPI_EN_DMA_TX
static void ConfigDmaSpiTx();
#endif
#else
#if _SPI_EN_DMA_TX
static void DmaSpiTx(u8 * tx_buff, u16 len);
#endif
#endif


#if (_SPI_USE_ASNYC)
// SPI0中断服务函数
WEAK USED void Spi0_Handler(void)
{
    u8 int_st = AP_SPI0->ISR;       // 读取中断状态寄存器，可能是读取清零
    u8 opt_len;
    // if(int_st & RECEIVE_FIFO_FULL)      // 接收FIFO满了
    // {
    //     opt_len = AP_SPI0->RXFTLR;  // FIFO中的数据长度，官方例程+1了，待验证
    //     if(mRxBuff && (mRxLen < mTransLen))
    //     {
    //         for(u8 i = 0; i < opt_len; i++) // 读出数据
    //         {
    //             mRxBuff[mRxLen++] = AP_SPI0->DataReg;
    //         }
    //     }
    //     else    // 不接收数据
    //     {
    //         for(u8 i = 0; i < opt_len; i++) // 读出无效数据
    //         {
    //             VOID (AP_SPI0->DataReg & 0xff);
    //             mRxLen++;
    //         }
    //     }
    //     if(mBusy && (mRxLen >= mTransLen)) // 接收完毕，说明发送也完毕了
    //     {
    //         TransCompleted();
    //         int_st = 0; // 不用再处理接下来的发送FIFO空事件(如果有)
    //     }
    // }
#if (_SPI_EN_DMA_TX == 0)
    if(int_st & TRANSMIT_FIFO_EMPTY)    // 发送FIFO空了(此时可能最后一个字节正在发送？？)
    {
        // 读取接收FIFO，读出收到的数据
        if(mRxBuff)
        {
            while(mRxLen < mTxLen)
            {
                mRxBuff[mRxLen++] = AP_SPI0->DataReg;
            }
        }
        else
        {
            while(mRxLen < mTxLen)
            {
                VOID (AP_SPI0->DataReg & 0xff);
                mRxLen++;
            }
        }
        opt_len = 8 - AP_SPI0->TXFLR;  // 获取FIFO可用空间，当前配置一般情况下都是8。
        if(mTxLen >= mTransLen) // 发送完毕了，不需要再发送
        {
            // if(mRxBuff && mRxLen < mTransLen) // 需要接收数据,可能还未或不会再触发接收FIFO满中断？？？
            // {
            //     while(AP_SPI0->SR & 0x01);  // 等待空闲
            //     opt_len = AP_SPI0->RXFTLR;  // FIFO中的数据长度，官方例程+1了，待验证
            //     for(u8 i = 0; i < opt_len; i++) // 读出数据
            //     {
            //         mRxBuff[mRxLen++] = AP_SPI0->DataReg; mRxBuff==NULL
            //     }
            // }
            TransCompleted();
        }
        else    // 还有数据需要传输
        {
            if(mTxBuff)
            {
                for(u8 i = 0; i < opt_len; i++)
                {
                    AP_SPI0->DataReg = mTxBuff[mTxLen++];
                    if(mTxLen == mTransLen)break;
                }
            }
            else    // 发 0
            {
                for(u8 i = 0; i < opt_len; i++)
                {
                    AP_SPI0->DataReg = 0;
                    mTxLen++;
                    if(mTxLen == mTransLen)break;
                }
            }
        }
    }
#endif
}
#endif

#if (_SPI_USE_ASNYC && (_SPI_EN_DMA_TX))
void Spi_EvDmaTransCompleted()
{
    if(mTxLen >= mTransLen) // 发送完毕了，不需要再发送
    {
        while(AP_SPI0->SR & 0x01);  // 等待空闲
        TransCompleted();
    }
    else    // 还有数据需要传输
    {
        ConfigDmaSpiTx();
    }
}
#endif

///////////////////////// 参照官方驱动添加，可能不需要 ///////////////////////////

static void Spi0SleepHandler()
{
    // if(AP_SPI0->SSIEN)
    {
        hal_clk_gate_disable(MOD_SPI0);
    }
}

static void Spi0WakeupHandler()
{
    NVIC_SetPriority((IRQn_Type)SPI0_IRQn, IRQ_PRIO_HAL);
}
///////////////////////////////////////////////////////////////////////////////////

void Spi_Init()
{
    ErrCode_t ret = ERR_NONE;

    hal_clk_gate_enable(MOD_SPI0);
    
    ///////////////////// 参照官方驱动添加，可能不需要 ///////////////////////////
    ret = hal_pwrmgr_register(MOD_SPI0, Spi0SleepHandler, Spi0WakeupHandler);
    if(ret != ERR_NONE)
    {
        while(1);
    }
    /////////////////////////////////////////////////////////////////////////////////
    AP_SPI0->SSIEN = 0; //DISABLE_SPI;
    SPI_MASTER_SELECT(0);       // 必须执行这一句之后才能对SPI进行配置，但是执行这一句会使能SPI。
    AP_SPI0->SSIEN = 0; //DISABLE_SPI;。    必须失能SPI才能对SPI进行配置。
    // REG_PERIPHERAL_MASTER_SELECT = 0;
    AP_SPI0->CR0 = (_SPI_DFS << 0)
                | (_SPI_FRF << 4)
                | (_SPI_CPHA << 6)
                | (_SPI_CPOL << 7)
                | (_SPI_TMOD << 8)
                | (_SPI_SLV_OE << 10)
                | (_SPI_SRL << 11);
    // AP_SPI0->CR1 = 
    AP_SPI0->BAUDR = _SPI_CLK_DIV; // 时钟分频，决定速度
    AP_SPI0->TXFTLR = 0; // 最大值为7，当发送FIFO内的数据个数小于等于这个值时触发中断。（内置硬件FIFO深度为8）
    AP_SPI0->RXFTLR = 7; // 最大值为7（7代表8个数据？），当接收FIFO内的数据个数大于等于这个值时触发中断。（内置硬件FIFO深度为8）
    AP_SPI0->IMR = (_SPI_IE_MST << 5)
                | (_SPI_IE_RXF << 4)
                | (_SPI_IE_RXO << 3)
                | (_SPI_IE_RXU << 2)
                | (_SPI_IE_TXO << 1)
                | (_SPI_IE_TXE << 0);
    AP_SPI0->SER = 1;      //enable slave device 自动控制片选???
    AP_SPI0->SSIEN = 1;  //ENABLE_SPI;

#if (_SPI_USE_ASNYC)
    JUMP_FUNCTION(SPI0_IRQ_HANDLER) = (uint32_t)&Spi0_Handler;
    NVIC_ClearPendingIRQ(SPI0_IRQn);        // 清除NVIC中断标志位
    NVIC_SetPriority(SPI0_IRQn, IRQ_PRIO_HAL);
    NVIC_EnableIRQ(SPI0_IRQn);
#endif
}

u8 Spi_TransByte(u8 tx_byte)
{
#if (_SPI_USE_ASNYC)
    while(mBusy);
#endif
    AP_SPI0->DataReg = tx_byte;
    while(AP_SPI0->SR_f.TFE == 0);  // 等待发送完毕
    while(AP_SPI0->SR_f.BUSY);
    return (u8)AP_SPI0->DataReg;
}

bool Spi_Trans(u8 * rx_buff, u8 * tx_buff, u16 len)
{
    if(rx_buff == NULL && tx_buff == NULL) return false;
    if(len == 0) return false;
#if (_SPI_EN_DMA_TX == 1)
    if(rx_buff == NULL)
    {
        while(len)
        {
            if(len > 2040)
            {
                DmaSpiTx(tx_buff, 2040);
                tx_buff += 2040;
                len -= 2040;
            }
            else
            {
                DmaSpiTx(tx_buff, len);
                len = 0;
            }
        }
    }
    else
#endif
    {
        while(len)
        {
            u8 opt_len;
            Mcu_DisableInt();
            opt_len = 8 - AP_SPI0->TXFLR;  // 获取FIFO可用空间，当前配置一般情况下都是8。
            if(tx_buff)
            {
                for(u8 i = 0; i < opt_len; i++)
                {
                    AP_SPI0->DataReg = *tx_buff;
                    tx_buff++;
                    len--;
                    if(len == 0)break;
                }
            }
            else    // 发 0
            {
                for(u8 i = 0; i < opt_len; i++)
                {
                    AP_SPI0->DataReg = 0;
                    len--;
                    if(len == 0)break;
                }
            }
            Mcu_EnableInt();
        }
    }
    return true;
}

#if (_SPI_USE_ASNYC)

bool Spi_StartTrans(u8 * rx_buff, u8 * tx_buff, u16 len)
{
    if(mBusy) return false;
    if(rx_buff == NULL && tx_buff == NULL) return false;
    if(len == 0) return false;

    mTxBuff = tx_buff;
    mRxBuff = rx_buff;
    mTransLen = len;
    mTxLen = 0;
    mRxLen = 0;

    mBusy = true;
    hal_pwrmgr_lock(MOD_SPI0);      // 阻止休眠

#if (_SPI_EN_DMA_RX == 1)
    ConfigDmaSpiRx();
    #error "not supported yet."
#endif
#if (_SPI_EN_DMA_TX == 1)
    ConfigDmaSpiTx();
#else
    Mcu_DisableInt();
    if(mTxBuff)
    {
        for(u8 i = 0; i < 8; i++)   // 一次可以写入最多8个数据进入FIFO
        {
            AP_SPI0->DataReg = mTxBuff[mTxLen++];
            if(mTxLen == mTransLen)break;
        }
    }
    else
    {
        for(u8 i = 0; i < 8; i++)
        {
            AP_SPI0->DataReg = 0;
            mTxLen++;
            if(mTxLen == mTransLen)break;
        }
    }
    AP_SPI0->IMR = 0x01;   // 允许发送FIFO空中断
    Mcu_EnableInt();
    // 在中断中处理FIFO空事件
#endif
    return true;
}

IN_LINE void Spi_SetCallback(spi_cb_t new_cb)
{
    mNotifyEvent = new_cb;
}

IN_LINE bool Spi_IsBusy()
{
    return mBusy;
}

#endif

// 以下内部函数

#if (_SPI_USE_ASNYC)
static void TransCompleted()
{
    AP_SPI0->IMR = 0;   // 禁止发送FIFO空中断
    mBusy = false;
    hal_pwrmgr_unlock(MOD_SPI0);
    if(mNotifyEvent)mNotifyEvent(SPI_EV_TRANS_COMPLETED);
}
#endif

#if (_SPI_USE_ASNYC)
#if (_SPI_EN_DMA_TX)
static void ConfigDmaSpiTx()
{
    DMA_CH_CFG_t cfgc;

    AP_SPI0->DMACR &= 0x01;

    u16 remin_len = mTransLen - mTxLen;
    if(remin_len > 2040)
    {
        cfgc.transf_size = 2040;
    }
    else
    {
        cfgc.transf_size = remin_len;
    }

    cfgc.sinc = DMA_INC_INC;
    cfgc.src_tr_width = DMA_WIDTH_BYTE;
    cfgc.src_msize = DMA_BSIZE_1;
    cfgc.src_addr = (uint32_t)mTxBuff + mTxLen;

    mTxLen += cfgc.transf_size;

    cfgc.dinc = DMA_INC_NCHG;
    cfgc.dst_tr_width = DMA_WIDTH_BYTE;
    cfgc.dst_msize = DMA_BSIZE_1;
    cfgc.dst_addr = (uint32_t)&(AP_SPI0->DataReg);

    cfgc.enable_int = true;

    AP_SPI0->DMACR |= 0x02;
    AP_SPI0->DMATDLR = 0;

    HalDMAConfigChannel(DMA_CH_0, &cfgc);
    HalDMAStartChannel(DMA_CH_0);   
    // 开始DMA传输，在DMA中断中处理传输结束事件
}
#endif
#else
#if (_SPI_EN_DMA_TX)
// 使用DMA发送数据，发完返回
// len : 发送数据字节数，最大2040
static void DmaSpiTx(u8 * tx_buff, u16 len)
{
    DMA_CH_CFG_t cfgc;

    AP_SPI0->DMACR &= 0x01;

    cfgc.transf_size = len;

    cfgc.sinc = DMA_INC_INC;
    cfgc.src_tr_width = DMA_WIDTH_BYTE;
    cfgc.src_msize = DMA_BSIZE_1;
    cfgc.src_addr = (uint32_t)tx_buff;

    cfgc.dinc = DMA_INC_NCHG;
    cfgc.dst_tr_width = DMA_WIDTH_BYTE;
    cfgc.dst_msize = DMA_BSIZE_1;
    cfgc.dst_addr = (uint32_t)&(AP_SPI0->DataReg);

    cfgc.enable_int = false;

    AP_SPI0->DMACR |= 0x02;
    AP_SPI0->DMATDLR = 0;

    HalDMAConfigChannel(DMA_CH_0, &cfgc);
    HalDMAStartChannel(DMA_CH_0);   
    HalDMAWaitChannelComplete(DMA_CH_0);
}
#endif
#endif









